home *** CD-ROM | disk | FTP | other *** search
- {*********************************************************}
- {* TstMedn *}
- {* Copyright (c) Julian M Bucknall 2000 *}
- {* All rights reserved. *}
- {*********************************************************}
- {* Algorithms Alfresco: Percentile calculator *}
- {*********************************************************}
-
- {Note: this unit is released as freeware. In other words, you are free
- to use this unit in your own applications, however I retain all
- copyright to the code. JMB}
-
- program TstMedn;
-
- {$IFDEF Windows}
- !! Error - 32-bit only
- {$ENDIF}
-
- {$APPTYPE CONSOLE}
-
- uses
- Windows,
- SysUtils;
-
- const
- MaxEIndex = 9999;
-
- type
- TDataElement = double;
-
- TLessFunction = function (const X, Y : TDataElement) : boolean;
- {function prototype to compare two items and return true if item X
- is STRICTLY LESS than item Y}
-
- type
- PDataArray = ^TDataArray;
- TDataArray = array [0..MaxEIndex] of TDataElement;
-
- procedure RandomizeSA(var SA : PDataArray);
- var
- i : integer;
- begin
- for i := 0 to MaxEIndex do
- SA^[i] := Trunc(Random * 1.0e6);
- end;
-
-
- function LessThan(const X, Y : TDataElement) : boolean;
- begin
- Result := X < Y;
- end;
-
- function CalcPercentile(var aItemArray : array of TDataElement;
- aLeft, aRight : integer;
- aLessThan : TLessFunction;
- aPosn : integer) : TDataElement;
- function Partition(L, R : integer): integer;
- var
- i, j : integer;
- Last : TDataElement;
- Temp : TDataElement;
- begin
- {set up the indexes}
- i := L;
- j := pred(R);
- {get the partition element}
- Last := aItemArray[R];
- {do forever (we'll break out of the loop when needed)}
- while true do begin
- {find the first element greater than or equal to the partition
- element from the left; note that our partition element will
- stop this loop}
- while aLessThan(aItemArray[i], Last) do
- inc(i);
- {find the first element less than the partition element from the
- right; check to break out of the loop if we hit the left
- element - we have no sentinel there}
- while aLessThan(Last, aItemArray[j]) do begin
- if (j = L) then
- Break;
- dec(j);
- end;
- {if we crossed get out of this infinite loop to swap the
- partition element into place}
- if (i >= j) then
- Break;
- {otherwise swap the two out-of-place elements}
- Temp := aItemArray[i];
- aItemArray[i] := aItemArray[j];
- aItemArray[j] := Temp;
- {and continue}
- inc(i);
- dec(j);
- end;
- {swap the partition element into place, return the dividing index}
- aItemArray[R] := aItemArray[i];
- aItemArray[i] := Last;
- Result := i;
- end;
- var
- DividingItem : integer;
- begin
- Assert(aLeft < aRight,
- 'the left index should be smaller than the right');
- Assert((aLeft <= aPosn) and (aPosn <= aRight),
- 'the position required should be between the left and right indexes');
- while (aLeft < aRight) do begin
- {partition about the final element in the set}
- DividingItem := Partition(aLeft, aRight);
- {select which part to further partition}
- if (DividingItem = aPosn) then begin
- Result := aItemArray[DividingItem];
- Exit;
- end;
- if (DividingItem < aPosn) then
- aLeft := succ(DividingItem)
- else
- aRight := pred(DividingItem);
- end;
- Result := aItemArray[aLeft];
- end;
-
- procedure UsualQuickSort(var aItemArray : array of TDataElement;
- aLeft, aRight : integer;
- aLessThan : TLessFunction);
- function Partition(L, R : integer): integer;
- var
- i, j : integer;
- Last : TDataElement;
- Temp : TDataElement;
- begin
- {set up the indexes}
- i := L;
- j := pred(R);
- {get the partition element}
- Last := aItemArray[R];
- {do forever (we'll break out of the loop when needed)}
- while true do begin
- {find the first element greater than or equal to the partition
- element from the left; note that our partition element will
- stop this loop}
- while aLessThan(aItemArray[i], Last) do
- inc(i);
- {find the first element less than the partition element from the
- right; check to break out of the loop if we hit the left
- element - we have no sentinel there}
- while aLessThan(Last, aItemArray[j]) do begin
- if (j = L) then
- Break;
- dec(j);
- end;
- {if we crossed get out of this infinite loop to swap the
- partition element into place}
- if (i >= j) then
- Break;
- {otherwise swap the two out-of-place elements}
- Temp := aItemArray[i];
- aItemArray[i] := aItemArray[j];
- aItemArray[j] := Temp;
- {and continue}
- inc(i);
- dec(j);
- end;
- {swap the partition element into place, return the dividing index}
- aItemArray[R] := aItemArray[i];
- aItemArray[i] := Last;
- Result := i;
- end;
- procedure QuickSortPrim(L, R : integer);
- var
- DividingItem : integer;
- begin
- {stop the recursion, if needed}
- if (R - L) <= 0 then
- Exit;
- {otherwise, partition about the final element in the set}
- DividingItem := Partition(L, R);
- {recursively quicksort the two subsets either side of the dividing
- element}
- QuicksortPrim(L, pred(DividingItem));
- QuicksortPrim(succ(DividingItem), R);
- end;
- begin
- {start it all off}
- QuicksortPrim(aLeft, aRight);
- end;
-
-
- var
- SA : PDataArray;
- i : integer;
- Value : TDataElement;
- begin
- try
- New(SA);
- try
- RandomizeSA(SA);
- Value := CalcPercentile(SA^, 0, MaxEIndex, LessThan,
- MaxEIndex div 2);
- writeln('Median is ', Value:10:2);
- Value := CalcPercentile(SA^, 0, MaxEIndex, LessThan,
- MaxEIndex div 20);
- writeln('5% percentile is ', Value:10:2);
- Value := CalcPercentile(SA^, 0, MaxEIndex, LessThan,
- (MaxEIndex * 19) div 20);
- writeln('95% percentile is ', Value:10:2);
-
- UsualQuickSort(SA^, 0, MaxEIndex, LessThan);
- writeln('Actual values using sorted array:');
- writeln(SA^[MaxEIndex div 2]:10:2);
- writeln(SA^[MaxEIndex div 20]:10:2);
- writeln(SA^[(MaxEIndex*19) div 20]:10:2);
- finally
- Dispose(SA);
- end;
- except
- on E: Exception do
- writeln(E.Message);
- end;
- readln;
- end.
-